package com.xiaojie.persistence.core.session;

import com.xiaojie.persistence.core.entity.BoundSql;
import com.xiaojie.persistence.core.entity.Configuration;
import com.xiaojie.persistence.core.entity.MappedStatement;
import com.xiaojie.persistence.core.entity.ParameterMapping;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Nick
 * @date 2021/5/6
 * @description
 */
public class SimpleExecutor implements Executor {

    Connection connection = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;
    BoundSql boundSql = null;


    private void preprocessing(Configuration configuration, MappedStatement mappedStatement) throws SQLException {
        // 1. 注册驱动，获取连接
        connection = configuration.getConnection();

        // 2. 获取sql语句
        boundSql = mappedStatement.getBoundSql();
        String sqlText = boundSql.getSqlText();

        // 3.获取预处理对象：preparedStatement
        preparedStatement = connection.prepareStatement(sqlText);
    }

    private void release() throws SQLException {
        if (resultSet != null) {
            resultSet.close();
        }
        if (preparedStatement != null) {
            preparedStatement.close();
        }
        if (connection != null) {
            connection.close();
        }
    }

    @Override
    public int update(Configuration configuration, MappedStatement mappedStatement, Object[] args) throws NoSuchFieldException, IllegalAccessException, SQLException {

        preprocessing(configuration, mappedStatement);

        // 4.获取参数,设置参数
        Class<?> parameterType = mappedStatement.getParameterType();
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();

        for (int i = parameterMappingList.size() - 1; i >= 0; i--) {

            ParameterMapping parameterMapping = parameterMappingList.get(i);
            String param = parameterMapping.getParameterName();

            // 利用反射获取字段
            Field declaredField = parameterType.getDeclaredField(param);
            // 去保护,设置强制访问
            declaredField.setAccessible(true);
            // 设置值
            Object o = declaredField.get(args[0]);
            preparedStatement.setObject(parameterMapping.getIndex(), o);
        }

        // 5. 执行sql
        int result = preparedStatement.executeUpdate();

        release();

        return result;
    }

    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] args) throws SQLException, NoSuchFieldException, IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException {

        preprocessing(configuration, mappedStatement);

        // 4.获取参数,设置参数
        Class<?> parameterType = mappedStatement.getParameterType();
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for (int i = parameterMappingList.size() - 1; i >= 0; i--) {

            ParameterMapping parameterMapping = parameterMappingList.get(i);
            String param = parameterMapping.getParameterName();

            // 利用反射获取字段
            Field declaredField = parameterType.getDeclaredField(param);
            // 去保护,设置强制访问
            declaredField.setAccessible(true);
            // 设置值
            Object o = declaredField.get(args[0]);
            preparedStatement.setObject(parameterMapping.getIndex(), o);
        }

        // 5. 执行sql
        ResultSet resultSet = preparedStatement.executeQuery();

        // 封装返回数据
        Class<?> resultType = mappedStatement.getResultType();
        List<E> resultList = new ArrayList<>();
        while (resultSet.next()) {
            Object o = resultType.newInstance();
            //元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {

                // 字段名
                String columnName = metaData.getColumnName(i);
                // 字段的值
                Object value = resultSet.getObject(columnName);

                //使用内省，根据数据库表和实体的对应关系，完成封装
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultType);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(o, value);
            }
            resultList.add((E) o);
        }

        release();

        return resultList;
    }
}
